// Copyright (C) 2018-2019 Intel Corporation
// SPDX-License-Identifier: Apache-2.0
//

#include <gtest/gtest.h>
#include <inference_engine/shape_infer/built-in/ie_built_in_holder.hpp>
#include <xml_net_builder.hpp>
#include <inference_engine/cnn_network_impl.hpp>
#include <inference_engine/shape_infer/ie_reshaper.hpp>
#include <cpp/ie_cnn_net_reader.h>
#include <inference_engine/debug.h>
#include "built_in_shape_infer_general_test.hpp"

using namespace InferenceEngine;
using namespace InferenceEngine::details;
using namespace ShapeInfer;

TEST_P(BuiltInShapeInferImplTest, impl) {
    auto impl = getShapeInferImpl(type);
    ASSERT_NE(nullptr, impl);
    ASSERT_NO_THROW(
            sts = impl->inferShapes(getBlobs(newInOutShapes.inDims), layerParams.data, blobs, outShapes, &resp));

    if (canInfer) {
        ASSERT_EQ(int(OK), sts) << resp.msg;
        ASSERT_EQ(newInOutShapes.outDims, outShapes);
    } else {
        ASSERT_EQ(GENERAL_ERROR, sts) << resp.msg;
    }
}

TEST_P(BuiltInShapeInferImplTest, reshaper) {
    auto cnnNetworkImplPtr = buildSingleLayerNetwork<3>(type, inOutShapes, &layerParams.data, layerDataName);
    auto reshaper = std::make_shared<Reshaper>(*cnnNetworkImplPtr);
    auto inputShapes = setInputShapes(*cnnNetworkImplPtr.get(), newInOutShapes.inDims);
    if (canInfer) {
        reshaper->run(inputShapes);
        checkNetworkInOut(*cnnNetworkImplPtr, newInOutShapes);
    } else {
        ASSERT_THROW(reshaper->run(inputShapes), InferenceEngine::details::InferenceEngineException);
    }
}

//TODO: use static variables for dimensions and parameters!!
//TODO: think about shorter instantiation
INSTANTIATE_TEST_CASE_P(
        BuiltInEqualImpls, BuiltInShapeInferImplTest,
        ::testing::Combine(
                ::testing::Values(LayerType("SoftMax"), LayerType("ELU"), LayerType("TanH"), LayerType("Sigmoid"),
                                  LayerType("Logistic"),
                                  LayerType("Normalize"), LayerType("Copy"), LayerType("Eltwise"),
                                  LayerType("ScaleShift"), LayerType("PowerFile"),
                                  LayerType("GRN"), LayerType("MVN"),
                                  LayerType("Abs"), LayerType("Acos"), LayerType("Acosh"), LayerType("Asin"),
                                  LayerType("Asinh"), LayerType("Atan"), LayerType("Atanh"),
                                  LayerType("Ceil"), LayerType("Cos"), LayerType("Cosh"), LayerType("Erf"), LayerType("Floor"),
                                  LayerType("HardSigmoid"), LayerType("Log"), LayerType("Neg"), LayerType("Reciprocal"),
                                  LayerType("Selu"), LayerType("Sign"), LayerType("Sin"), LayerType("Sinh"),
                                  LayerType("Softplus"), LayerType("Softsign"), LayerType("Tan"), LayerType("LogSoftmax")),
                ::testing::Values(InOutShapes({{{1, 1, 1, 1}},
                                               {{1, 1, 1, 1}}})),
                ::testing::Values(NewInOutShapes({{{1, 3, 228, 228}},
                                                  {{1, 3, 228, 228}}})),
                ::testing::Values(MapParams(MapStrStr())),
                ::testing::Values(LayerDataName("data")),
                ::testing::Values(CanInfer(true)))
);

INSTANTIATE_TEST_CASE_P(
        BuiltInMultiImpls, BuiltInShapeInferImplTest,
        ::testing::Combine(
                ::testing::Values(LayerType("Mul"), LayerType("Eltwise"), LayerType("Add"), LayerType("Div")),
                ::testing::Values(InOutShapes({{{1, 1, 1, 1}, {1, 1, 1, 1}},
                                               {{1, 1, 1, 1}}})),
                ::testing::Values(NewInOutShapes({{{1, 3, 228, 228}, {1, 3, 228, 228}},
                                                  {{1, 3, 228, 228}}})),
                ::testing::Values(MapParams(MapStrStr())),
                ::testing::Values(LayerDataName("data")),
                ::testing::Values(CanInfer(true)))
);

INSTANTIATE_TEST_CASE_P(
        BuiltInGeneralImpls, BuiltInShapeInferImplTest,
        ::testing::Values(
                ::testing::make_tuple(LayerType("LRN"),
                                      InOutShapes({{{1, 1, 1, 1}},
                                                   {{1, 1, 1, 1}}}),
                                      NewInOutShapes({{{1, 3, 228, 228}},
                                                      {{1, 3, 228, 228}}}),
                                      MapParams(MapStrStr({{"alpha",      "9.9999997e-05"},
                                                           {"beta",       "0.75"},
                                                           {"local-size", "5"},
                                                           {"region",     "across"}})),
                                      LayerDataName("norm_data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ReLU"),
                                      InOutShapes({{{1, 1, 1, 1}},
                                                   {{1, 1, 1, 1}}}),
                                      NewInOutShapes({{{1, 3, 228, 228}},
                                                      {{1, 3, 228, 228}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"negative_slope", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Power"),
                                      InOutShapes({{{1, 1, 1, 1}},
                                                   {{1, 1, 1, 1}}}),
                                      NewInOutShapes({{{1, 3, 228, 228}},
                                                      {{1, 3, 228, 228}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"shift", "1"},
                                                                                             {"power", "1"},
                                                                                             {"scale", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Clamp"),
                                      InOutShapes({{{1, 1, 1, 1}},
                                                   {{1, 1, 1, 1}}}),
                                      NewInOutShapes({{{1, 3, 228, 228}},
                                                      {{1, 3, 228, 228}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"min", "1"},
                                                                                             {"max", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("BatchNormalization"),
                                      InOutShapes({{{1, 1, 1, 1}},
                                                   {{1, 1, 1, 1}}}),
                                      NewInOutShapes({{{1, 3, 228, 228}},
                                                      {{1, 3, 228, 228}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"epsilon", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("InnerProduct"),
                                      InOutShapes({{{1, 3, 228, 228}},
                                                   {{1, 1000}}}),
                                      NewInOutShapes({{{1, 3, 228, 228}},
                                                      {{1, 1000}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"out-size", "1000"}})),
                                      LayerDataName("fc_data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("FullyConnected"),
                                      InOutShapes({{{1, 3}},
                                                   {{1, 1000}}}),
                                      NewInOutShapes({{{BATCH, 3}},
                                                      {{BATCH, 1000}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"out-size", "1000"}})),
                                      LayerDataName("fc_data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Permute"),
                                      InOutShapes({{{2, 3, 4, 5}},
                                                   {{4, 3, 5, 2}}}),
                                      NewInOutShapes({{{10, 3, 4, 5}},
                                                      {{4,  3, 5, 10}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"order", "2,1,3,0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Reshape"),
                                      InOutShapes({{{1, 34452}},
                                                   {{2, 5742, 6}}}),
                                      NewInOutShapes({{{2, 34458}},
                                                      {{2, 5743, 6}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"dim", "0,-1,6"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Reshape"),
                                      InOutShapes({{{1,   1, 300, 4}},
                                                   {{300, 4}}}),
                                      NewInOutShapes({{{1,   1, 500, 4}},
                                                      {{500, 4}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"dim", "-1,4"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Flatten"),
                                      InOutShapes({{{2, 1, 4, 5}},
                                                   {{40}}}),
                                      NewInOutShapes({{{4, 1, 4, 5}},
                                                      {{80}}}),
                                      MapParams(MapParams(MapStrStr())),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("PriorBoxClustered"), // TODO 5D test
                                      InOutShapes({ {{2, 1, 4, 5}, {2, 4, 5, 6}},
                                                   {{1, 2, 400}}}),
                    NewInOutShapes({ {{4, 1, 5, 5}, {3, 5, 6, 3}},
                                                      {{1, 2, 500}} }),
                                      MapParams(MapStrStr(
                                              std::map<std::string, std::string>{{"width",  "86.000000,13.000000,57.000000,39.000000,68.000000"},
                                                                                 {"clip",   "0"},
                                                                                 {"flip",   "1"},
                                                                                 {"offset", "0.5"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("PriorBox"),
                                      InOutShapes({{{1, 256, 1, 1}, {1, 3, 300, 300}},
                                                   {{1, 2,   16}}}),
                                      NewInOutShapes({{{2, 256, 1, 1}, {2, 3, 300, 300}},
                                                      {{1, 2,   16}}}),
                                      MapParams(MapStrStr(
                                              std::map<std::string, std::string>{{"min_size",     "264"},
                                                                                 {"max_size",     "315"},
                                                                                 {"clip",         "0"},
                                                                                 {"flip",         "1"},
                                                                                 { "offset",       "0.5" },
                                                                                 {"aspect_ratio", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("PriorBox"),
                                      InOutShapes({{{2, 512, 32, 32}, {2, 3, 512, 512}},
                                                   {{1, 2,   16384}}}),
                                      NewInOutShapes({{{2, 512, 32, 32}, {2, 3, 512, 512}},
                                                      {{1, 2,   16384}}}),
                                      MapParams(MapStrStr(
                                              std::map<std::string, std::string>{{"min_size",        "35.84,52.46464"},
                                                                                 {"max_size",        ""},
                                                                                 {"clip",            "0"},
                                                                                 {"step",            "16"},
                                                                                 {"flip",            "0"},
                                                                                 {"offset",          "0.5"},
                                                                                 {"aspect_ratio",    "1.0,2.0,0.5"},
                                                                                 {"scale_all_sizes", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("PriorBox"),
                                      InOutShapes({{{2, 512, 32, 32}, {2, 3, 512, 512}},
                                                   {{1, 2,   32768}}}),
                                      NewInOutShapes({{{2, 512, 32, 32}, {2, 3, 512, 512}},
                                                      {{1, 2,   28672}}}),
                                      MapParams(MapStrStr(
                                              std::map<std::string, std::string>{{"min_size",        "35.84,52.46464"},
                                                                                 {"max_size",        ""},
                                                                                 {"clip",            "0"},
                                                                                 {"step",            "16"},
                                                                                 {"offset",          "0.5"},
                                                                                 {"flip",            "1"},
                                                                                 {"aspect_ratio",    "1.0,2.0,0.5"},
                                                                                 {"scale_all_sizes", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("DetectionOutput"),
                                      InOutShapes({{{2, 1, 4,   5}, { 2, 1, 4,   5 }, { 2, 1, 4,   5 }},
                                                   {{2, 1, 200, 7}}}),
                                      NewInOutShapes({{{4, 1, 5,   5}, { 4, 1, 5,   5 }, { 4, 1, 5,   5 }},
                                                      {{1, 1, 800, 7}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"keep_top_k",    "200"},
                                                                                             {"num_classes",   "21"},
                                                                                             {"nms_threshold", "0.44999998807907104"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Interp"),
                                      InOutShapes({{{2, 2, 33,  65}},
                                                   {{2, 2, 257, 513}}}),
                                      NewInOutShapes({{{2, 2, 33,  65}},
                                                      {{2, 2, 257, 513}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"align_corners", "1"},
                                                                                             {"height",        "257"},
                                                                                             {"pad_beg",       "0"},
                                                                                             {"pad_end",       "0"},
                                                                                             {"width",         "513"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Interp"),
                                      InOutShapes({{{2, 2, 33, 65}},
                                                   {{2, 2, 66, 513}}}),
                                      NewInOutShapes({{{2, 2, 33, 65}},
                                                      {{2, 2, 66, 513}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"align_corners", "1"},
                                                                                             {"factor",        "2"},
                                                                                             {"width",         "513"},
                                                                                             {"pad_beg",       "0"},
                                                                                             {"pad_end",       "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Interp"),
                                      InOutShapes({{{2, 2, 33,  65}},
                                                   {{2, 2, 257, 130}}}),
                                      NewInOutShapes({{{2, 2, 33,  65}},
                                                      {{2, 2, 257, 130}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"align_corners", "1"},
                                                                                             {"factor",        "2"},
                                                                                             {"height",        "257"},
                                                                                             {"pad_beg",       "0"},
                                                                                             {"pad_end",       "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ROIPooling"),
                                      InOutShapes({{{2,   3, 4, 5}, {150, 5}},
                                                   {{150, 3, 6, 6}}}),
                                      NewInOutShapes({{{4,   1, 5, 5}, {150, 5}},
                                                      {{150, 1, 6, 6}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"pooled_h",      "6"},
                                                                                             {"pooled_w",      "6"},
                                                                                             {"spatial_scale", "0.062500"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Upsampling"),
                                      InOutShapes({{{1, 3, 4, 5}},
                                                   {{1, 3, 8, 10}}}),
                                      NewInOutShapes({{{2, 1, 5,  5}},
                                                      {{2, 1, 10, 10}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"scale", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("PSROIPooling"),
                                      InOutShapes({{{1, 3, 4, 5}, {150, 5}},
                                                   {{150, 2, 6, 6}}}),
                                      NewInOutShapes({{{2,   1, 5, 5}, {200, 5}},
                                                      {{200, 2, 6, 6}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"output_dim", "2"},
                                                                                             {"group_size", "6"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("SimplerNMS"),
                                      InOutShapes({{{1,   3, 4, 5}, {1, 3, 4, 5}, {1, 3}},
                                                   {{150, 5}}}),
                                      NewInOutShapes({{{2,   1, 5, 5}, {2, 1, 5, 5}, {1, 3}},
                                                      {{150, 5}}}),
                                      MapParams(
                                              MapStrStr(std::map<std::string, std::string>{{"post_nms_topn", "150"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Concat"),
                                      InOutShapes({{{1, 3, 5, 5}, {1, 2, 5, 5}},
                                                   {{1, 5, 5, 5}}}),
                                      NewInOutShapes({{{2, 1, 5, 5}, {2, 1, 5, 5}},
                                                      {{2, 2, 5, 5}}}),
                                      MapParams(
                                              MapStrStr(std::map<std::string, std::string>{{"post_nms_topn", "150"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Crop"),
                                      InOutShapes({{{1, 3, 5, 5}, {7, 7, 2, 3}},
                                                   {{1, 5, 2, 3}}}),
                                      NewInOutShapes({{{2, 1, 5, 6}, {7, 7, 2, 3}},
                                                      {{2, 1, 2, 3}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",   "2,3"},
                                                                                             {"offset", "2,1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Crop"),
                                      InOutShapes({{{1, 3, 5, 5}},
                                                   {{1, 5, 2, 3}}}),
                                      NewInOutShapes({{{2, 1, 5, 6}},
                                                      {{2, 1, 2, 1}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",   "2,3"},
                                                                                             {"offset", "2,1"},
                                                                                             {"dim",    "2,1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                // offset is too big
                ::testing::make_tuple(LayerType("Crop"),
                                      InOutShapes({{{1, 3, 5, 5}},
                                                   {{1, 5, 2, 3}}}),
                                      NewInOutShapes({{{2, 1, 5, 4}},
                                                      {{2, 1, 2, 1}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",   "2,3"},
                                                                                             {"offset", "3,4"},
                                                                                             {"dim",    "2,1"}})),
                                      LayerDataName("data"),
                                      CanInfer(false)),
                ::testing::make_tuple(LayerType("Crop"),
                                      InOutShapes({{{1, 3, 5, 5}},
                                                   {{1, 5, 2, 3}}}),
                                      NewInOutShapes({{{2, 1, 5, 6}},
                                                      {{2, 1, 1, 4}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",       "2,3"},
                                                                                             {"crop_begin", "2,1"},
                                                                                             {"crop_end",   "2,1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Tile"),
                                      InOutShapes({{{1, 3, 5, 5}},
                                                   {{1, 9, 5, 5}}}),
                                      NewInOutShapes({{{2, 1, 5, 6}},
                                                      {{2, 3, 5, 6}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",  "1"},
                                                                                             {"tiles", "3"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Split"),
                                      InOutShapes({{{1, 4, 5, 5}},
                                                   {{1, 2, 5, 5}, {1, 2, 5, 5}}}),
                                      NewInOutShapes({{{2, 4, 5, 6}},
                                                      {{2, 2, 5, 6}, {2, 2, 5, 6}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",      "1"},
                                                                                             {"out_sizes", "2,2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Slice"),
                                      InOutShapes({{{1, 6, 5, 5}},
                                                   {{1, 2, 5, 5}, {1, 4, 5, 5}}}),
                                      NewInOutShapes({{{2, 6, 5, 6}},
                                                      {{2, 2, 5, 6}, {2, 4, 5, 6}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",      "1"},
                                                                                             {"out_sizes", "2,4"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Proposal"),
                                      InOutShapes({{{1,   12, 34, 62}, {1, 24, 34, 62}, {1, 6}},
                                                   {{200, 5}}}),
                                      NewInOutShapes({{{2,   1, 5, 5}, {2, 1, 5, 5}, {1, 6}},
                                                      {{400, 5}}}),
                                      MapParams(
                                              MapStrStr(std::map<std::string, std::string>{{"post_nms_topn", "200"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ReorgYolo"),
                                      InOutShapes({{{1, 64,  26, 26}},
                                                   {{1, 256, 13, 13}}}),
                                      NewInOutShapes({{{2, 8,  6, 6}},
                                                      {{2, 32, 3, 3}}}),
                                      MapParams(
                                              MapStrStr(std::map<std::string, std::string>{{"stride", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("RegionYolo"),
                                      InOutShapes({{{1, 125, 13, 13}},
                                                   {{1, 21125}}}),
                                      NewInOutShapes({{{20, 125, 16, 13}},
                                                      {{20, 26000}}}),
                                      MapParams(MapStrStr({{"axis",       "1"},
                                                           {"end_axis",   "-1"},
                                                           {"do_softmax", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ArgMax"),
                                      InOutShapes({{{1, 3, 1025, 2049}},
                                                   {{1, 3, 1025, 100}}}),
                                      NewInOutShapes({{{20, 3, 1025, 2049}},
                                                      {{20, 3, 1025, 100}}}),
                                      MapParams(MapStrStr({{"out_max_val", "1"},
                                                           {"top_k",       "100"},
                                                           {"axis",        "-1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ArgMax"),
                                      InOutShapes({{{1, 3, 1025, 2049}},
                                                   {{1, 3, 100,  2049}}}),
                                      NewInOutShapes({{{20, 3, 1025, 2049}},
                                                      {{20, 3, 100,  2049}}}),
                                      MapParams(MapStrStr({{"out_max_val", "1"},
                                                           {"top_k",       "100"},
                                                           {"axis",        "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ArgMax"),
                                      InOutShapes({{{1, 3}},
                                                   {{1, 2, 100}}}),
                                      NewInOutShapes({{{20, 3}},
                                                      {{20, 2, 100}}}),
                                      MapParams(MapStrStr({{"out_max_val", "1"},
                                                           {"top_k",       "100"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ArgMax"),
                                      InOutShapes({{{1, 3}},
                                                   {{1, 1, 100}}}),
                                      NewInOutShapes({{{20, 3}},
                                                      {{20, 1, 100}}}),
                                      MapParams(MapStrStr({{"out_max_val", "0"},
                                                           {"top_k",       "100"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{15, 10}, {10, 20}, {15, 20}},
                                                   {{15, 20}}}),
                                      NewInOutShapes({{{20, 15}, {15, 25}, {20, 25}},
                                                      {{20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{15, 10}, {10, 20}, {15, 20}},
                                                   {{15, 20}}}),
                                      NewInOutShapes({{{20, 15}, {10, 25}, {20, 25}},
                                                      {{20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(false)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{15, 10}, {10, 20}, {15, 20}},
                                                   {{15, 20}}}),
                                      NewInOutShapes({{{20, 15}, {15, 25}, {15, 25}},
                                                      {{20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(false)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{15, 10}, {10, 20}},
                                                   {{15, 20}}}),
                                      NewInOutShapes({{{20, 15}, {15, 25}},
                                                      {{20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{15, 10}, {10, 20}},
                                                   {{15, 20}}}),
                                      NewInOutShapes({{{20, 15}, {10, 25}},
                                                      {{20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(false)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{3, 3, 15, 10}, {3, 3, 10, 20}, {3, 3, 15, 20}},
                                                   {{3, 3, 15, 20}}}),
                                      NewInOutShapes({{{4, 1, 20, 15}, {4, 1, 15, 25}, {4, 1, 20, 25}},
                                                      {{4, 1, 20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Gemm"),
                                      InOutShapes({{{3, 3, 15, 10}, {3, 1, 10, 20}, {3, 1, 15, 20}},
                                                   {{3, 3, 15, 20}}}),
                                      NewInOutShapes({{{4, 2, 20, 15}, {4, 2, 15, 25}, {4, 1, 20, 25}},
                                                      {{4, 2, 20, 25}}}),
                                      MapParams(MapStrStr({{"alpha",       "1"},
                                                           {"beta",        "1"},
                                                           {"transpose_a", "false"},
                                                           {"transpose_b", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Pad"),
                                      InOutShapes({{{3, 3,  15, 10}},
                                                   {{9, 11, 25, 22}}}),
                                      NewInOutShapes({{{4,  2,  20, 15}},
                                                      {{10, 10, 30, 27}}}),
                                      MapParams(MapStrStr({{"pads_begin", "1,2,3,4"},
                                                           {"pads_end",   "5,6,7,8"},
                                                           {"pad_mode",   "edge"},
                                                           {"pad_value",  "1.0f"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Pad"),
                                      InOutShapes({{{10, 10, 15, 10}},
                                                   {{16, 18, 25, 22}}}),
                                      NewInOutShapes({{{20, 30, 40, 50}},
                                                      {{26, 38, 40, 50}}}),
                                      MapParams(MapStrStr({{"pads_begin", "1,2,0,0"},
                                                           {"pads_end",   "5,6,0,0"},
                                                           {"pad_mode",   "reflect"},
                                                           {"pad_value",  "1.0f"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Pad"),
                                      InOutShapes({{{10, 10, 15, 10}},
                                                   {{16, 18, 25, 22}}}),
                                      NewInOutShapes({{{4,  2,  20, 15}},
                                                      {{10, 10, 30, 27}}}),
                                      MapParams(MapStrStr({{"pads_begin", "1,2,3,4"},
                                                           {"pads_end",   "5,6,7,8"},
                                                           {"pad_mode",   "reflect"},
                                                           {"pad_value",  "1.0f"}})),
                                      LayerDataName("data"),
                                      CanInfer(false))
        )
);

// There are gtest limitation on tests number: 50
INSTANTIATE_TEST_CASE_P(
        BuiltInGeneralImpls2, BuiltInShapeInferImplTest,
        ::testing::Values(
                ::testing::make_tuple(LayerType("Gather"),
                                      InOutShapes({{{7, 16}, {1, 25}},
                                                   {{1, 25, 16}}}),
                                      NewInOutShapes({{{7,  16}, {12, 25}},
                                                      {{12, 25, 16}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Gather"),
                                      InOutShapes({{{7, 16}, {1, 25}},
                                                   {{7, 1, 25}}}),
                                      NewInOutShapes({{{7, 16}, {12, 25}},
                                                      {{7, 12, 25}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Gather"),
                                      InOutShapes({{{7, 16}, {1, 25}},
                                                   {{7, 1, 25}}}),
                                      NewInOutShapes({{{7, 16}, {12, 25}},
                                                      {{7, 12, 25}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis", "-1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("CTCGreedyDecoder"),
                                      InOutShapes({{{88, 1,  48, 1}},
                                                   {{1,  88, 1,  1}}}),
                                      NewInOutShapes({{{88, 2,  48, 1}},
                                                      {{2,  88, 1,  1}}}),
                                      MapParams(MapStrStr()),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("CTCGreedyDecoder"),
                                      InOutShapes({{{88, 1, 71}, {88, 1}},
                                                   {{1,  88, 1, 1}}}),
                                      NewInOutShapes({{{88, 2, 71}, {88, 2}},
                                                      {{2,  88, 1,  1}}}),
                                      MapParams(MapStrStr()),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Reshape"),
                                      InOutShapes({{{1, 2}},
                                                   {{1, 1}}}),
                                      NewInOutShapes({{{1, 2}},
                                                      {{1, 1}}}),
                                      MapParams(MapStrStr(
                                              std::map<std::string, std::string>{{"dim", "1,1"}})),  // dim doesn't match input
                                      LayerDataName("data"),
                                      CanInfer(false)),
                ::testing::make_tuple(LayerType("Flatten"),
                                      InOutShapes({{{2, 1, 4, 5}},
                                                   {{40}}}),
                                      NewInOutShapes({{{4, 1, 4, 5}},
                                                      {{80}}}),
                                      MapParams(MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",     "0"},
                                                                                                       {"end_axis", "-1"}}))),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Flatten"),
                                      InOutShapes({{{2, 2, 4, 5}},
                                                   {{2, 8, 5}}}),
                                      NewInOutShapes({{{4, 2, 4, 5}},
                                                      {{4, 8, 5}}}),
                                      MapParams(MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",     "1"},
                                                                                                       {"end_axis", "2"}}))),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Flatten"),
                                      InOutShapes({{{2, 2, 4, 5}},
                                                   {{2, 40}}}),
                                      NewInOutShapes({{{4, 2, 4, 5}},
                                                      {{4, 40}}}),
                                      MapParams(
                                              MapParams(MapStrStr(std::map<std::string, std::string>{{"axis", "1"}}))),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Flatten"),
                                      InOutShapes({{{2, 2, 4, 5}},
                                                   {{4, 4, 5}}}),
                                      NewInOutShapes({{{4, 2, 4, 5}},
                                                      {{8, 4, 5}}}),
                                      MapParams(MapParams(
                                              MapStrStr(std::map<std::string, std::string>{{"end_axis", "1"}}))),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Interp"),
                                      InOutShapes({{{2, 2, 100, 16}},
                                                   {{2, 2, 25,  4}}}),
                                      NewInOutShapes({{{2, 2, 201, 33}},
                                                      {{2, 2, 50,  8}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"align_corners", "1"},
                                                                                             {"factor",        "0.25"},
                                                                                             {"pad_beg",       "0"},
                                                                                             {"pad_end",       "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Interp"),
                                      InOutShapes({{{2, 2, 100, 16}},
                                                   {{2, 2, 100, 16}}}),
                                      NewInOutShapes({{{2, 2, 101, 33}},
                                                      {{2, 2, 101, 33}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"align_corners", "1"},
                                                                                             {"shrink_factor", "1.5"},
                                                                                             {"zoom_factor",   "1.5"},
                                                                                             {"pad_beg",       "0"},
                                                                                             {"pad_end",       "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ShuffleChannels"),
                                      InOutShapes({{{1, 2, 3, 4}},
                                                   {{1, 2, 3, 4}}}),
                                      NewInOutShapes({{{2, 4, 4, 7}},
                                                      {{2, 4, 4, 7}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis",  "1"},
                                                                                             {"group", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("DepthToSpace"),
                                      InOutShapes({{{4, 2, 3}},
                                                   {{1, 4, 6}}}),
                                      NewInOutShapes({{{8, 3, 4}},
                                                      {{2, 6, 8}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"block_size", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("SpaceToDepth"),
                                      InOutShapes({ { { 1, 4, 6 } },
                                                    { { 4, 2, 3 } } }),
                                      NewInOutShapes({ { { 2, 6, 8 } },
                                                       { { 8, 3, 4 } } }),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{ {"block_size", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("ReverseSequence"),
                                      InOutShapes({{{3, 4, 5}, {3}},
                                                   {{3, 4, 5}}}),
                                      NewInOutShapes({{{4, 8, 9}, {4}},
                                                      {{4, 8, 9}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"seq_axis",   "1"},
                                                                                             {"batch_axis", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("SparseSegmentSum"),
                                      InOutShapes({{{5, 2, 3}, {10}, {10}},
                                                   {{10, 2, 3}}}),
                                      NewInOutShapes({{{20, 5, 4}, {17}, {17}},
                                                      {{17, 5, 4}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Select"),
                                      InOutShapes({ { {2, 4, 4 }, {2, 4, 4}, {2, 4, 4} },
                                                    { {2, 4, 4} } }),
                                      NewInOutShapes({ { {2, 4, 4 }, {4, 4, 4}, {4, 4, 4} },
                                                       { { 4, 4, 4 } } }),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("RegionYolo"),
                                      InOutShapes({{{1,       125, 13, 13}},
                                                   {{1 * 125, 13,  13}}}),
                                      NewInOutShapes({{{20,       125, 16, 13}},
                                                      {{20 * 125, 16,  13}}}),
                                      MapParams(MapStrStr({{"axis",       "0"},
                                                           {"end_axis",   "1"},
                                                           {"do_softmax", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("RegionYolo"),
                                      InOutShapes({{{1,            125, 13, 13}},
                                                   {{1 * 125 * 13, 13}}}),
                                      NewInOutShapes({{{20,            125, 16, 13}},
                                                      {{20 * 125 * 16, 13}}}),
                                      MapParams(MapStrStr({{"axis",       "0"},
                                                           {"end_axis",   "2"},
                                                           {"do_softmax", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("RegionYolo"),
                                      InOutShapes({{{1, 125,                13, 13}},
                                                   {{1, (80 + 4 + 1) * 125, 13, 13}}}),
                                      NewInOutShapes({{{20, 125,                16, 13}},
                                                      {{20, (80 + 4 + 1) * 3, 16, 13}}}),
                                      MapParams(MapStrStr({{"axis",       "1"},
                                                           {"end_axis",   "-1"},
                                                           {"do_softmax", "0"},
                                                           {"classes",    "80"},
                                                           {"coords",     "4"},
                                                           {"mask",       "6,7,8"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Upsampling"),
                                      InOutShapes({{{1, 3, 4, 5, 6}},
                                                   {{1, 3, 8, 10, 12}}}),
                                      NewInOutShapes({{{2, 1, 7, 5, 5}},
                                                      {{2, 1, 14, 10, 10}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"scale", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("OneHot"),
                                      InOutShapes({{{1, 3, 4}},
                                                   {{1, 3, 4, 3}}}),
                                      NewInOutShapes({{{2, 1, 7}},
                                                      {{2, 1, 7, 3}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"depth", "3"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("OneHot"),
                                      InOutShapes({{{1, 3, 5}},
                                                   {{1, 11, 3, 5}}}),
                                      NewInOutShapes({{{2, 4, 7}},
                                                      {{2, 11, 4, 7, }}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"depth", "11"}, {"axis", "1"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Quantize"),
                                      InOutShapes({{{1, 64, 10, 10}, {1, 64, 1, 1}, {1, 64, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
                                                   {{1, 64, 10, 10}}}),
                                      NewInOutShapes({{{2, 128, 10, 10}, {1, 128, 1, 1}, {1, 128, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 1}},
                                                      {{2, 128, 10, 10}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{ {"levels", "2"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Unique"),
                                      InOutShapes({{{5}},
                                                   {{5}, {5}}}),
                                      NewInOutShapes({{{25}},
                                                      {{25}, {25}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"sorted", "false"},
                                                                                             {"return_inverse", "true"},
                                                                                             {"return_counts", "false"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Unique"),
                                      InOutShapes({{{5}},
                                                   {{5}, {5}, {5}}}),
                                      NewInOutShapes({{{25}},
                                                      {{25}, {25}, {25}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"sorted", "false"},
                                                                                             {"return_inverse", "true"},
                                                                                             {"return_counts", "true"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("Scatter"),
                                      InOutShapes({{{3, 3}, {2, 3}},
                                                   {{3,3}}}),
                                      NewInOutShapes({{{4,  4}, {3, 4}},
                                                      {{4,4}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"axis", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true)),
                ::testing::make_tuple(LayerType("NonMaxSuppression"),
                                      InOutShapes({{{1, 2, 4}, {1, 3, 2}},
                                                   {{6, 3}}}),
                                      NewInOutShapes({{{2, 5, 4}, {2, 3, 5}},
                                                      {{30, 3}}}),
                                      MapParams(MapStrStr(std::map<std::string, std::string>{{"center_point_box", "0"}})),
                                      LayerDataName("data"),
                                      CanInfer(true))
        )
);

class LayerValidatorNegativeTests : public BuiltInShapeInferImplTest {
};

TEST_P(LayerValidatorNegativeTests, reshaper) {
    ASSERT_THROW(buildSingleLayerNetwork<3>(type, inOutShapes, &layerParams.data, layerDataName),
                 InferenceEngine::details::InferenceEngineException);
}

// TODO: test using MR!1690
INSTANTIATE_TEST_CASE_P(
        Reshape, LayerValidatorNegativeTests,
        ::testing::Combine(
                ::testing::Values(LayerType("Reshape")),
                ::testing::Values(InOutShapes({{{1,   1, 300, 4}},
                                               {{300, 4}}})),
                ::testing::Values(NewInOutShapes({{{1,   1, 500, 4}},
                                                  {{500, 4}}})),
                ::testing::Values(
                        MapParams(MapStrStr(
                                std::map<std::string, std::string>{{"dim", "0,-2,6"}})),  // can't be less the -1
                        MapParams(MapStrStr(
                                std::map<std::string, std::string>{{"dim", "0,-1,-1"}}))),  // single -1 is expected
                ::testing::Values(LayerDataName("data")),
                ::testing::Values(CanInfer())
        )
);

